home *** CD-ROM | disk | FTP | other *** search
- Windows '95 and Flat Thunking
-
-
-
- Contents
- ========
-
- Overview of Win32/Win32s thunking mechanisms
- The flat thunking mechanism
- A flat thunking example
- Debugging notes
- Recommended reading
-
-
-
- Overview of Win32/Win32s thunking mechanisms
- ============================================
-
- For the Win32s, Windows NT and Windows 95 platforms, Microsoft has provided
- us with three thunking mechanisms: universal thunking, general thunking, and
- flat thunking.
-
- Universal thunking is available to Win32 applications running on Win32s, a
- 32-bit translation layer which allows 32-bit programs to be run on 16-bit
- Windows 3.x. Primarily, this mechanism accomodates 32-bit applications which
- are running in a hybrid environment of 32-bit modules inherent to the
- application itself and 16-bit modules inherent to the 16-bit environment.
-
- General thunking is available on both Windows NT and Windows 95. Howevever,
- it is intended for use primarily on Windows NT and Microsoft documentation
- advises that applications using General Thunking should not be expected to
- have the same behaviour when run under Windows NT and Windows 95 due to
- differences in the operating systems. General thunking allows a 16-bit
- application to call into a 32-bit DLL, but not the reverse. Its usefulness
- seems to be primarily to support incremental porting of a 16-bit application
- to 32 bits, where the DLLs of the application would be ported first, and the
- executable only in a later revision, after all DLLs have been ported.
-
- Flat thunking is available only under Windows 95. Flat thunking allows
- bi-directional calls between 16- and 32-bit modules. Using flat thunking
- requires that 16- and 32-bit modules be rebuilt, linking in special thunk
- modules created using the Microsoft thunk compiler and an assembler. Like
- general thunking, it seems primarily designed to support incremental porting
- of applications. An advantage of flat thunking is that you can call in
- either direction, and can even have a single call chain that moves back and
- forth between a pair of 16- and 32-bit modules. However, a module can only
- connect to one other module, so a bit of planning may be wise for larger
- projects.
-
-
- The flat thunking mechanism
- ===========================
-
- Flat thunking requires that we create a special assembly module, assemble it
- twice to a 16-bit and a 32-bit object, then link those two objects into the
- corresponding 16- and 32-bit modules (DLLs or executables). The assembly
- file is created by first writing a thunk script, which is very much like a C
- header file containing function prototypes and type definitions. The
- Microsoft thunk compiler then translates the script into a single assembly
- source file containing both 16- and 32-bit code. Two assembler macros, IS_16
- and IS_32, allow the file to be assembled to the two distinct objects. Next,
- code is added to the 16- and 32-bit modules (DLL's or executables) to allow
- a runtime connection to be established between the two modules. Finally, a
- few modifications are made to the module definition files for each of the
- targets and the targets are brought up to date.
-
- To examine the flat thunking mechanism, we will use an example of a 32-bit
- DLL calling a function in a 16-bit DLL, which is what the example "Thunker"
- does. In the function names referred to below, the "ThunkObj_" prefix is a
- result of names generated by the Microsoft thunk compiler which it derives
- from the filename of our thunk script. These are not standard names that you
- can look up in a help file.
-
- First, the 32-bit DLL must establish a connection with the 16-bit DLL. This
- is done by calling ThunkObj_ThunkConnect32 and is most easily done from the
- 32-bit DLL's DllEntryPoint. This call causes the 16-bit DLL to be loaded. If
- the 16-bit DLL is marked for Windows 4.0, then the system will call its
- DllEntryPoint. A quirk is that the 16-bit DLL must have both a LIbMain and a
- DllEntryPoint. LibMain is called only when the DLL is loaded, while
- DllEntryPoint is called both on load and unload.
-
- In DllEntryPoint, the 16-bit DLL establishes its side of the connection with
- the 32-bit DLL by calling ThunkObj_ThunkConnect16. The process of
- establishing this connection invokes code in the thunk modules which
- initializes jump tables for the exported functions. In the thunker example,
- the 16-bit DLL does not call into the 32-bit DLL, so the code generated for
- the 16-bit thunk module contains code only to initialize this jump table,
- after which, the 16-bit thunk module's job is done.
-
- Calls can now be made between the 32- and 16-bit DLL's. Given that the
- 16-bit DLL exports a function Fun16, a call from the 32-bit DLL can be
- described. First, the call from the 32-bit DLL is actually resolved to a
- Fun16 procedure in the ThunkObj assembly module (which is why we never do
- anything to import Func16 from the 16-bit DLL). This thunk procedure fixes
- up the stack to account for a change from standard call to pascal calling
- convention, accomodates changes in data sizes (ie, size of int), and
- translates pointers (between 32-bit flat pointers expected in the 32-bit
- module and the selector:offset format expected in the 16-bit module). The
- thunk then uses the jump table to call directly into the 16-bit DLL.
-
- The connection is broken by calling the same ThunkObj_ThunkConnect32 and
- ThunkObj_ThunkConnect16 functions, but passing DLL_PROCESS_DETACH as the
- last parameter.
-
-
- A flat thunking example
- =======================
-
- The example, Thunker, provides a shell demonstration of flat thunking. In
- this example, a 32-bit application uses a 32-bit DLL which calls into a
- 16-bit DLL. Microsoft documentation recommends that we thunk between DLL's
- rather than directly from an executable to a DLL as we can later port the
- 16-bit DLL and then simply replace the 16- and 32-bit thunking DLL's with
- one new 32-bit DLL. Since flat thunking has the limitation of a module being
- able to connect to only one other module, it is the only way to have a
- 32-bit executable effectively use multiple 16-bit DLL's, or vice-versa.
- Also, the model of interfacing thru a DLL is conducive to a multi-platform
- application which will determine at runtime which interface DLL to use
- (since no one thunking mechanism will work on all Windows 32-bit platforms).
-
- The example demonstrates all the basic requirements for coding and building
- an application using flat thunking. I strongly recommend you read thru the
- makefile and all the associated source - including the module definition
- files. Omitting a step or requirement can lead to strange runtime failures
- that can be difficult to debug.
-
- Further information on flat thunking is available in the Win32 online help
- file. Go to index item Thunk Compiler and page thru the "chapter". Further
- information on general and universal thunking is available in the Microsoft
- Win32 SDK documentation, and on the Microsoft Developer's Network.
-
- This example demonstrates calling from a 32-bit application into a 16-bit
- DLL. Per recommendations in Microsoft technical documentation, this is done
- by calling thru a 32-bit DLL. The example presumes that all calls into the
- 16-bit DLL are made only from the 32-bit DLL and describes modifications
- from that starting point. The application model looks like this:
-
- ┌────────┐ ┌────────┐ ┌─────────┐
- │ 32.exe │───>│ 32.dll │ │ 16.dll │
- └────────┘ │ ┌─────┴────┐ ┌───┴──────┐ │
- └──┤ to32.obj │────>│ to16.obj ├──┘
- └──────────┘ └──────────┘
-
- At runtime, the executable causes the 32-bit DLL to be loaded and that DLL
- establishes a connection by calling a Thunk_Connect function exported from
- the 16-bit thunk object. The 16-bit side then initializes a jump table which
- is passed back to the 32-bit DLL. When a call is made to a function exported
- from the 16-bit DLL, the call from the 32-bit DLL call actually resolves to
- a function in the 32-bit thunk object. This function then performs the steps
- neccassary to translate the call (ie, allowing for different sizes in data
- types) and then calls thru the jump table to the actual function in the
- 16-bit DLL. On return, control returns to the thunk function in the 32-bit
- thunk object, which does some translating of return values and structures
- passed by reference and ultimately returns to the original caller.
-
- To implement a thunking application, you must:
-
- 1) Create a thunk script providing declarative information about
- functions and data structures being thunked.
-
- 2) Generate assembly source from the thunk script using the Microsoft
- thunk compiler.
-
- 3) Assemble the thunk module to a 16-bit object module which will be
- linked into the 16-bit DLL exporting the user functions.
-
- 4) Assemble the thunk module to a 32-bit object module which will be
- linked into the 32-bit DLL that will be importing the user functions.
-
- 5) Modify the 16-bit DLL to provide a DllEntryPoint function which will
- be called to establish a connection with the 32-bit DLL.
-
- 6) Modify the 16-bit DLL's module definition file to:
- a) Add SUBSYSTEM 4.0 so the DLL is marked for Windows 4.0;
- b) Export DllEntryPoint, the 16-bit thunk data, and those functions
- you wish to call from the 32-bit world; and,
- c) Import C16THKSL01 and THUNKCONNECT16 from kernel.
-
- 7) Modify the 32-bit DLL to establish and break the connection with the
- 16-bit DLL at runtime.
-
- 8) Modify the 32-bit DLL's module definition file to:
- a) import ThunkConnect32@24 from kernel32
- b) export the 32-bit thunk data.
-
- Special notes:
-
- The 16-bit DLL must be marked for subsystem 4.0, else the added
- DllEntryPoint function will not be called to establish the connection. This
- can be done with the linker /V switch or with a SUBSYSTEM entry in the
- module definition file. Of course, having done this, the DLL will only work
- on Windows 95.
-
- When assembling the thunk module, you must use the assembler's /ml option
- with /DIS_32 and must not use it with /DIS_16. This is because the thunk
- compiler always generates case sensitive symbols. By default, the 32-bit
- side of the thunk mechanism uses standard call calling convention, for which
- the case sensitivity must be preserved. The 16-bit side of the thunk
- mechanism uses pascal calling convention and the symbols there should be
- forced to upper case. (Since the thunk compiler generates functions which
- manipulate parameters being passed between the 16- and 32-bit modules, do
- not attempt to override the default calling conventions.)
-
- You may notice that in the 32-bit thunk module, the generated names for
- functions are created by adding to the function name an at sign ('@')
- followed by a numeric value representing the width of the parameter list.
- This is the naming convention that Microsoft uses for stdcall calling
- convention and the names are generated by the Microsoft thunk compiler. To
- resolve them, Turbo Assembler, when given the -utthk switch, automatically
- generates aliases which match the Borland naming convention. You can examine
- both forms of these names using tdump with this syntax:
-
- tdump -oipubdef -oiextdef -oialias thkobj16.obj
- tdump -oipubdef -oiextdef -oialias thkobj32.obj
-
- The 32-bit module can be built with debug info, but the 16-bit module cannot
- as tasm32 generates only 32-bit debug info which the 16-bit linker does not
- expect. Also, the -utthk switch is unique to tasm32 and is rejected by tasm
- and tasmx.
-
-
- Debugging notes
- ===============
-
- Turbo Debugger for Win32 cannot step from 32-bit code into 16-bit code. It
- is possible to step thru the 32-bit thunk module in source assembly or in
- the debuggers CPU view. While in the CPU view, do not attempt to step into a
- call thru the thunk table into the 16-bit DLL as it will cause TD32 to hang.
- Attempting to debug with available tool sets at this level will likely be
- very challenging and require very good Assembly language skills.
-
-
- Recommended reading
- ===================
- Additional information on thunking under various Microsoft Windows'
- environments can be found on the Microsoft Developers Network CD and in the
- Microsoft Win32 SDK documentation. Of particular interst are the articles
- "Flat Thunking Mechanics" and "How to Debug Flat Thunks" on the MSDN CD.
-
-